﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Nemerle2.Compiler
{
  /// <summary>
  /// Represent a inferred or explicitly specified a Nemerle type.
  /// </summary>
  public variant FixedType : NType
  {
    #region Options
    /// Represent a class type with given type parameters, like "string" or "list[int]".
    | Class
      {
        info : ITypeInfo;
        args : list[NType];
      }

    /// In code like:
    ///      class Foo[T] { bar : list[T]; }
    ///   The "T" in "list[T]" would be represented by a ITypeParameterInfoRef.
    ///   You can get ITypeParameterInfo objects from the type builder of Foo class.
    | TypeParameterInfoRef { ITypeParameterInfo : ITypeParameterInfo; }

    /// Represent a function type "from -> to".
    ///   If the function takes more than one parameter, the "from" is a tuple.
    | Fun { from : FixedType.Tuple; to : NType; }

    /// Represent a tuple, like "int * string * list[float]"
    | Tuple { args : list[NType]; }

    /// Represent an array type, rank refers to the number of dimensions.
    | Array { t : NType; rank : int; }

    /// These are used in signatures of functions taking ref/out parameter.
    ///   For example in:
    ///      foo (x : ref int) : void
    ///   the x has type "Ref(Class(InternalType.Int32_tc,[]))".
    ///   They are not used in function types though!
    | Ref { t : NType; }
    | Out { t : NType; }

    /// Represent the unit type.
    | Void

    /** Used when given value is required to have all the listed types.

       Invariant 1: the types listed cannot be in the subtyping relation
       pairwise.

       Invariant 2: there can be only Class() objects inside.

       This type is not expressible in the .NET type system directly,
       it can be however expressed with type variable bounds in some
       cases. **/
    | Intersection { types : list[FixedType]; }

    #endregion

    public override ToString() : string
    {
      match(this)
      {
        | Class(info, [])           => info.Name
        | Class(info, args)         => $<#$(info.Name)[..$(args; ", ")]#>
        | TypeParameterInfoRef(varRef)  => $<#!!$varRef#>
        | Fun(from, to)             => $<#$from -> $to#>
        | Tuple(args)               => $<#..$(args; " * ")#>
        | Array(t, rank)            => $<#array[$rank, $t]#>
        | Ref(t)                    => $<#ref $t#>
        | Out(t)                    => $<#out $t#>
        | Void                      => "void"
        | Intersection(types)       => $<#INT: (..$(types; ", "))#>
      }
    }
  }
}